﻿using System.Collections;
using System.Collections.Immutable;
using System.Diagnostics;

namespace DimensionsHelper.LanguageService.Collections;

internal sealed class ArrayBuilder<T>(int size) : IReadOnlyList<T>
{
    private static readonly ObjectPool<ArrayBuilder<T>> ArrayBuilderPool = CreatePool();

    private readonly ImmutableArray<T>.Builder _builder = ImmutableArray.CreateBuilder<T>(size);

    private readonly ObjectPool<ArrayBuilder<T>>? _pool;


    public ArrayBuilder() : this(8)
    {
    }


    private ArrayBuilder(ObjectPool<ArrayBuilder<T>> pool) : this()
    {
        _pool = pool;
    }


    public T this[int index]
    {
        get => _builder[index];

        set => _builder[index] = value;
    }


    public int Count => _builder.Count;


    public IEnumerator<T> GetEnumerator()
    {
        return _builder.GetEnumerator();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return _builder.GetEnumerator();
    }


    public static ObjectPool<ArrayBuilder<T>> CreatePool(int size)
    {
        ObjectPool<ArrayBuilder<T>>? pool = null;
        pool = new ObjectPool<ArrayBuilder<T>>(size, () => new ArrayBuilder<T>(pool!));
        return pool;
    }


    public static ObjectPool<ArrayBuilder<T>> CreatePool()
    {
        return CreatePool(128);
    }


    public static ArrayBuilder<T> GetInstance()
    {
        return ArrayBuilderPool.Allocate();
    }


    public static ArrayBuilder<T> GetInstance(int size)
    {
        ArrayBuilder<T> builder = ArrayBuilderPool.Allocate();
        builder.EnsureCapacity(size);
        return builder;
    }


    public void EnsureCapacity(int capacity)
    {
        if (_builder.Capacity < capacity)
        {
            _builder.Capacity = capacity;
        }
    }


    public void Clear()
    {
        _builder.Clear();
    }


    public bool Contains(T item)
    {
        return _builder.Contains(item);
    }


    public int IndexOf(T item)
    {
        return _builder.IndexOf(item);
    }


    public int IndexOf(T item, IEqualityComparer<T> comparer)
    {
        return _builder.IndexOf(item, 0, _builder.Count, comparer);
    }


    public int FindIndex(int start, int count, Predicate<T> predicate)
    {
        int end = Math.Min(start + count, _builder.Count);
        for (int i = start; i < end; i++)
        {
            if (predicate(_builder[i]))
            {
                return i;
            }
        }

        return -1;
    }


    public int FindIndex(int start, Predicate<T> predicate)
    {
        return FindIndex(start, _builder.Count, predicate);
    }


    public int FindIndex(Predicate<T> predicate)
    {
        return FindIndex(0, predicate);
    }


    public int FindIndex<TArg>(int start, int count, Func<T, TArg, bool> predicate, TArg arg)
    {
        int end = Math.Min(start + count, _builder.Count);
        for (int i = start; i < end; i++)
        {
            if (predicate(_builder[i], arg))
            {
                return i;
            }
        }

        return -1;
    }


    public int FindIndex<TArg>(int start, Func<T, TArg, bool> predicate, TArg arg)
    {
        return FindIndex(start, _builder.Count, predicate, arg);
    }


    public int FindIndex<TArg>(Func<T, TArg, bool> predicate, TArg arg)
    {
        return FindIndex(0, predicate, arg);
    }


    public bool Remove(T item)
    {
        return _builder.Remove(item);
    }


    public void RemoveAt(int index)
    {
        _builder.RemoveAt(index);
    }


    public void RemoveRange(int start, int count)
    {
        _builder.RemoveRange(start, count);
    }


    public void RemoveAll(Predicate<T> predicate)
    {
        _builder.RemoveAll(predicate);
    }


    public T Pop()
    {
        Debug.Assert(Count > 0);
        T last = _builder.Last();
        _builder.RemoveAt(_builder.Count - 1);
        return last;
    }


    public void Reverse()
    {
        _builder.Reverse();
    }


    public void Sort()
    {
        _builder.Sort();
    }


    public void Sort(Comparison<T> comparison)
    {
        _builder.Sort(comparison);
    }


    public void Sort(IComparer<T> comparer)
    {
        _builder.Sort(comparer);
    }


    public T First()
    {
        Debug.Assert(_builder.Count > 0);
        return _builder[0];
    }


    public T? FirstOrDefault()
    {
        return _builder.Count > 0 ? _builder[0] : default;
    }


    public T Last()
    {
        Debug.Assert(_builder.Count > 0);
        return _builder[^1];
    }


    public T? LastOrDefault()
    {
        return _builder.Count > 0 ? _builder[^1] : default;
    }


    public bool Any()
    {
        return _builder.Count > 0;
    }


    public ImmutableArray<T> ToImmutableArray()
    {
        return _builder.ToImmutableArray();
    }


    public T[] ToArray()
    {
        return _builder.ToArray();
    }


    public void Free()
    {
        if (_pool == null)
        {
            return;
        }

        if (Count > 0)
        {
            Clear();
        }

        _pool.Free(this);
    }
}